package huanzhao.erp.controller;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import huanzhao.erp.domain.annotation.RateLimit;
import huanzhao.erp.domain.entity.Staff;
import huanzhao.erp.domain.enumerate.error.ProcessError;
import huanzhao.erp.domain.po.Result;
import huanzhao.erp.domain.vo.NotificationVO;
import huanzhao.erp.domain.vo.StaffVO;
import huanzhao.erp.domain.vo.TeamVO;
import huanzhao.erp.other.config.ProjectConfig;
import huanzhao.erp.service.*;
import huanzhao.erp.util.ValidateCodeUtil;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.Range;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;


/**
 * 杂项控制
 *
 * @author April
 * 2024/1/4 20:09
 * @version 1.0
 */
@RestController
@RequestMapping("/api")
@Validated
@Slf4j
public class MiscController {
    public final static ConcurrentHashMap<Long, SseEmitter> emitters = new ConcurrentHashMap<>();
    final HttpSession session;
    private final BasicService basicService;
    private final NotificationService notificationService;
    private final StaffService staffService;
    private final TeamService teamService;
    private final ProjectConfig projectConfig;
    private final PhoneService phoneService;

    public MiscController(HttpSession session, BasicService basicService, NotificationService notificationService, StaffService staffService, TeamService teamService, ProjectConfig projectConfig, PhoneService phoneService) {
        this.session = session;
        this.basicService = basicService;
        this.notificationService = notificationService;
        this.staffService = staffService;
        this.teamService = teamService;
        this.projectConfig = projectConfig;
        this.phoneService = phoneService;
    }

    /**
     * 获取验证码图片
     *
     * @param response 响应
     *                 2024/1/5 12:09:48
     */
    @GetMapping("captcha")
    @RateLimit(period = 80, rate = 15, msg = "获取验证码次数过多")
    public void getCaptchaImg(HttpServletResponse response) {
        try {
            response.setContentType("image/png");
            // 禁用缓存
            response.setHeader("Cache-Control", "no-cache");
            // 立即过期
            response.setHeader("Expires", "0");
            response.setHeader("Pragma", "no-cache");
            // getRandomCodeImage方法会直接将生成的验证码图片写入response
            ValidateCodeUtil.getRandomCodeImage(session, response);
        } catch (Exception e) {
            log.error("获取验证码图片失败", e);
        }
    }

    /**
     * SSE订阅接口
     *
     * @return SseEmitter
     * 2024/6/11 下午11:20:10
     */
    @GetMapping("/subscribe")
    public SseEmitter subscribe() {
        SseEmitter emitter = new SseEmitter();
        // 根据当前登录用户类型将emitter放入对应的map中
        long recipientId = basicService.getCurrentStaffId();
        emitters.put(recipientId, emitter);
        log.info("建立SSE连接，消息接收者id：{}", recipientId);

        emitter.onCompletion(() -> notificationService.removeEmitter(recipientId));

        emitter.onTimeout(() -> {
            log.info("SSE连接超时，消息接收者id：{}", recipientId);
            notificationService.removeEmitter(recipientId);
        });

        emitter.onError((e) -> {
            log.error("SSE连接时异常，消息接收者id：{}，异常原因：{}", recipientId, e.getMessage());
            notificationService.removeEmitter(recipientId);
        });

        // 找出所有未推送的消息进行推送
        long total = notificationService.getUnpushedNotificationsCount(recipientId);
        List<NotificationVO> notifications = total > 0 ? notificationService.getUnpushedNotificationsInLimit(recipientId, projectConfig.getNotificationCount()) : Collections.emptyList();
        try {
            // 当消息数量过多时，返回部分消息
            Page<NotificationVO> page = new Page<>();
            page.setTotal(total);
            page.setRecords(notifications);
            emitter.send(SseEmitter.event().name("init").data(basicService.parseObj2Json(page)));
            // 推送完未推送的消息后标记为已推送
            notificationService.pushedNotifications(recipientId);
            log.info("SSE初次推送，消息接收者id：{}，消息数量：{}", recipientId, total);
        } catch (IOException e) {
            log.error("SSE初次推送时发生异常，消息接收者id：{}，消息数量：{}", recipientId, total, e);
        }
        return emitter;
    }


    /**
     * 按条件查询当前员工的通知消息
     *
     * @param type     消息类型：即将超时、已超时、新工单、工单批复结果
     * @param pageNum  当前页码
     * @param pageSize 每页记录数量
     * @return IPage<Notification>
     * 2024/6/11 下午11:13:04
     */
    @GetMapping("/notification")
    public Result<IPage<NotificationVO>> getCurrentUserNotifications(@RequestParam(required = false) String type, @RequestParam int pageNum, @RequestParam int pageSize) {
        return Result.ok(notificationService.getNotificationsByCondition(basicService.getCurrentStaffId(), type, pageNum, pageSize));
    }

    /**
     * 获取用户的未读消息数量
     *
     * @return Long
     * 2024/7/4 下午2:48:17
     */
    @GetMapping("/notification/unreadMount")
    public Result<Long> getUserUnreadNotificationMount() {
        return Result.ok(notificationService.getUnreadNotificationsCount(basicService.getCurrentStaffId()));
    }

    /**
     * 当前用户已读特定消息
     *
     * @param id 消息id
     * @return Result<?>
     * 2024/6/11 下午11:13:55
     */
    @PutMapping("/notification/{id}")
    public Result<?> readNotification(@PathVariable Long id) {
        return notificationService.readNotification(id, basicService.getCurrentStaffId());
    }

    /**
     * 当前用户已读全部消息
     *
     * @return Result<?>
     * 2024/6/11 下午11:13:57
     */
    @PutMapping("/notification")
    public Result<?> readAllNotifications() {
        return notificationService.readAllNotifications(basicService.getCurrentStaffId());
    }

    /**
     * 登录
     *
     * @param phone    手机号
     * @param password 密码
     * @param captcha  图片验证码
     * @return Result<?>
     * 2024/6/11 下午11:14:00
     */
    @RateLimit(period = 200, rate = 5, msg = "登录重试次数过多")
    @PostMapping("/login")
    public Result<?> login(@RequestParam @NotBlank String phone,
                           @RequestParam @NotBlank String password,
                           @RequestParam @NotNull @Length(min = 4, max = 4) String captcha) {
        // 校验图片验证码
        Result<?> checkPicVerifyCode = basicService.checkCaptcha(captcha);
        if (checkPicVerifyCode.isError()) {
            return checkPicVerifyCode;
        }
        return staffService.login(phone, password);
    }

    /**
     * 登出
     *
     * @return Result<?>
     * 2024/6/11 下午9:46:34
     */
    @DeleteMapping("/logout")
    public Result<?> logout() {
        long recipientId = basicService.getCurrentStaffId();
        SseEmitter sseEmitter = notificationService.removeEmitter(recipientId);
        StpUtil.logout();
        session.invalidate();
        if (sseEmitter != null) {
            sseEmitter.complete();
        }
        return Result.ok("登出成功");
    }

    /**
     * 获取团队内的员工
     *
     * @param teamId 团队id
     * @param type   员工类型：录入、质检、上架，为空表示查询该团队的所有员工
     * @return List<Staff>
     * 2024/6/17 下午8:56:16
     */
    @GetMapping("/team/staff")
    public Result<List<StaffVO>> getStaffInTeamByType(@RequestParam long teamId, @RequestParam @Range(min = 1, max = 3) int type, @RequestParam @NotBlank String taskType) {
        if (teamService.notExists(teamId)) {
            return Result.error(ProcessError.TEAM_NOT_FOUND);
        }
        boolean up = false;
        boolean qc = false;
        boolean input = false;
        switch (type) {
            case 1 -> up = true;
            case 2 -> qc = true;
            case 3 -> input = true;
        }
        return Result.ok(staffService.getStaffInTeamByType(teamId, taskType, up, qc, input));
    }

    /**
     * 查询所有团队基本信息
     *
     * @return List<Team>
     * 2024/6/17 下午8:57:11
     */
    @GetMapping("/team/basic")
    public Result<List<TeamVO>> getAllTeams() {
        return Result.ok(teamService.getAllTeamBasic());
    }

    /**
     * 按类型查询员工
     *
     * @param type 员工类型：维修、库管、采购
     * @return List<Staff>
     * 2024/6/17 下午8:58:12
     */
    @GetMapping("/process/staff")
    public Result<List<StaffVO>> getStaffByType(@RequestParam @Range(min = 1, max = 3) int type, @RequestParam @NotBlank String taskType) {
        boolean maintainer = false;
        boolean warehouser = false;
        boolean purchaser = false;
        switch (type) {
            case 1 -> maintainer = true;
            case 2 -> warehouser = true;
            case 3 -> purchaser = true;
        }
        return Result.ok(staffService.getStaffByType(taskType, maintainer, warehouser, purchaser));
    }

    /**
     * 根据imei查询处于某状态的手机id列表
     * 该接口用于前端参数传递imei时，可获得处于某状态的手机数量，从而帮助用户更快的获得反馈
     *
     * @param state 手机的状态
     * @return Result<List < Long>>
     * 2024/10/13 10:17:36
     */
    @GetMapping("/phone/state")
    public Result<List<Long>> getPhoneIdsByImeiAndState(@RequestParam @NotBlank String imei,
                                                        @RequestParam @NotNull @Size(min = 1) String[] state) {
        return phoneService.getPhoneIdsByImeiAndState(imei, state);
    }

    /**
     * 获取当前登录的用户状态
     *
     * @return LoginStatus
     * 2024/6/18 下午10:15:09
     */
    @GetMapping("/login/status")
    public Result<StaffVO> getLoginStatus() {
        Staff staff = basicService.getCurrentStaff();
        return Result.ok(BeanUtil.copyProperties(staff, StaffVO.class));
    }

}
